package com.qf.controller;


import com.alibaba.fastjson.JSONObject;
import com.qf.captcha.CaptchaCodeManager;
import com.qf.pojo.DtsAdmin;
import com.qf.service.PermService;
import com.qf.service.RoleService;
import com.qf.util.*;
import com.qf.util.Base64;
import com.qf.util.UUID;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

@RestController
@CrossOrigin("*")
@RequestMapping("/auth")
//解决跨域问题
//，前后端，协议、域名、端口号不一致时，属于跨域
public class AuthController {
    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermService permService;

    @Autowired
    private ApplicationContext context;

    private HashMap<String,String> systemPermMap = null;

    @RequestMapping("/captchaImage")
    public Object getCodeImage(HttpServletResponse response) {
        //1、生成4位随机验证码
        String code = VerifyCodeUtils.generateVerifyCode(4);
        //2、生成验证码唯一id
        String uuid = UUID.randomUUID().toString(true);

        //3、将验证码存储到内存中
        boolean flag = CaptchaCodeManager.addToCache(uuid, code, 5);
        //4、判断验证码是否存储成功
        if (!flag) {
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
        }
        //5、生成验证码图片
        ByteOutputStream out = new ByteOutputStream();
        try {
            VerifyCodeUtils.outputImage(100, 40, out, code);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //6、将验证码唯一id和图片内容返回
        Map<String, String> resultMap = new HashMap<>();
        /*
        out.toByteArray()是得到一个与out输出流大小一样的新的缓冲区，
        并包含out的字节数组值,保证不浪费内存空间，又保证字节流能完整
        输出
        */
        resultMap.put("img", Base64.encode(out.toByteArray()));

        resultMap.put("uuid", uuid);
        return ResponseUtil.ok(resultMap);
    }

    /**
     * 登录
     *
     * @param param
     * @return
     */
    @RequestMapping("/login")
    public Object login(@RequestBody String param) {
        //1、提取json中的参数
        //用户页面上输入的验证码
        String code = JacksonUtil.parseString(param, "code");
        System.out.println(code);
        //获取username
        String username = JacksonUtil.parseString(param, "username");
        //获取password
        String password = JacksonUtil.parseString(param, "password");
        //获取uuid
        String uuid = JacksonUtil.parseString(param, "uuid");
        //2、非空判断
        if (StringUtils.isEmpty(username)) {
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }
        if (StringUtils.isEmpty(password)) {
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }
        if (StringUtils.isEmpty(code)) {
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }
        if (StringUtils.isEmpty(uuid)) {
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
        }
        //3、校验用户的验证码是否正确，如果不正确返回提示信息
        String cachedCode = CaptchaCodeManager.getCachedCaptcha(uuid);
        if (!code.equalsIgnoreCase(cachedCode)) {
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }
        //4、创建shiro的subject对象
        Subject subject = SecurityUtils.getSubject();

        // 5、创建shiro的用户名，密码对象
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        //6、调用shirod 的login登录方法触发shiro的自定义realm,进行校验、授权
        try {
            subject.login(token);
        } catch (UnknownAccountException e) {
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        } catch (AuthenticationException e) {
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_AUTH);
        }

        // 7、获取用户登录成功后，在shiro框架的session会话中的唯一id，返回
        return ResponseUtil.ok(subject.getSession().getId());
    }

    @GetMapping("/info")
    public Object info() {
//        1、获取shiro框架的subject对象
        Subject subject = SecurityUtils.getSubject();
//        2、从subject对象中获取当前登录用户实体对象
        DtsAdmin admin = (DtsAdmin) subject.getPrincipal();
//        3、获取用户头像信息
        Map<String, Object> map = new HashMap<>();
        String avatar = admin.getAvatar();
        String username = admin.getUsername();
        map.put("username", username);
        map.put("avatar", avatar);
//        4、获取当前用户的角色id集合
        Integer[] roleIds = admin.getRoleIds();
//        5、根据角色id集合获取当前登录用户的角色集合
        Set<String> roles = roleService.queryRolesByIds(roleIds);
//        6、根据角色id集合获取当前登录用户的权限集合
        Set<String> perms = permService.queryPermByRoleIds(roleIds);
//        7、封装数据
        map.put("roles", roles);
        map.put("perms",toAPI(perms));
        return ResponseUtil.ok(map);
    }

    /**
     * 将权限字符串集合转换成API形式的权限集合
     * @param perms
     * @return
     */
    private Collection<String> toAPI(Set<String> perms){
        //封装所有的controller方法, 这些方法应该具有什么样的权限字符串才能够访问
        //systemPermMap中的数据例如: key是admin:brand:list value: POST /admin/brand/list
        if (systemPermMap == null || systemPermMap.size() == 0) {
            systemPermMap = new HashMap<>();
            //设置我们controller所在的包名
            String basicPackage = "com.qf";
            //通过反射机制, 获取指定包中的controller中带自定义权限注解的方法
            List<Permission> systemPersionList = PermissionUtil.listPermission(context, basicPackage);
            //遍历
            for (Permission permission : systemPersionList) {
                //权限字符串数组
                String[] perm = permission.getRequiresPermissions().value();
                //对应的能够访问的url路径
                String api = permission.getApi();
                //key是权限字符串, value是能够访问的url路径
                systemPermMap.put(perm[0], api);
            }
        }

        //封装用来存放当前用户能够访问的url地址
        //例如:  POST /admin/admin/delete
        Collection<String> apis = new HashSet<>();

        //遍历传入的这个登录用户的权限字符串集合
        for (String perm : perms) {
            //根据权限字符串, 获取对应的url访问路径
            String api = systemPermMap.get(perm);
            apis.add(api);

            if (perm.equals("*")) {
                apis.clear();
                apis.add("*");
                return apis;
            }
        }
        return apis;
    }



    /*
     * 用户注销
     */

    @PostMapping("/logout")
    public Object login() {
        Subject currentUser = SecurityUtils.getSubject();
        currentUser.logout();

       // logger.info("【请求结束】系统管理->用户注销,响应结果:{}", JSONObject.toJSONString(currentUser.getSession().getId()));
        return ResponseUtil.ok();
    }

}
